#include "precompiled.h"
#include "allocator_test_reflection.h"
#include "exception_tests.h"


template <size_t N>
bond::blob BlobFromStringLiteral(const char (&literal)[N])
{
    // string literals have an extra NUL char at the end that isn't part of
    // the payload, so adjust the size.
    return bond::blob(literal, static_cast<uint32_t>(N - 1));
}


template <typename Reader, typename T, typename BondedType>
void Deserialize(bond::blob data)
{
    BOOST_STATIC_ASSERT((std::is_same<BondedType, T>::value || std::is_same<BondedType, void>::value));

    Reader reader(data);
    bond::bonded<T> bonded(reader);

    T obj;

    // De-serialize the object
    bond::bonded<BondedType>(bonded).Deserialize(obj);
}


template <typename Writer>
struct InvalidPayload
{
    template <typename T>
    static bond::blob PartialField()
    {
        // Payload indicates a field of type T and id 2 and then ends.
        // Attempt to de-serialize or skip the field leads to Eof exception.
        typename Writer::Buffer buffer;
        Writer writer(buffer);

        writer.WriteStructBegin(bond::Metadata(), false);
        writer.WriteFieldBegin(bond::get_type_id<T>::value, 2);

        return buffer.GetBuffer();
    }

    template <typename T>
    static bond::blob OomField()
    {
        // Payload indicates a container field with id 2 and _UI32_MAX elements and then ends.
        // Attempt to de-serialize leads to OOM exception.
        typename Writer::Buffer buffer;
        Writer writer(buffer);

        writer.WriteStructBegin(bond::Metadata(), false);
        writer.WriteFieldBegin(bond::get_type_id<T>::value, 2);
        writer.WriteContainerBegin(0xffffffff, bond::get_type_id<typename bond::element_type<T>::type>::value);

        return buffer.GetBuffer();
    }
};


template <typename Reader, typename Writer>
struct MissingFieldExceptionTest
{
    template <typename T>
    void operator()(const T&)
    {
        // The test structure contains 2 required fields of type T. The invalid payload
        // contains only partial second field. With dynamic parser the missing required
        // field leads to missing field exception, and skipping the field during stack
        // unrolling results in Eof exception which is internally caught.
        // With static parser there is no missing field exception and only Eof exception.
        typedef BondStructRequired<T> Type;

        bool        exception;
        bond::blob  payload;

        payload = InvalidPayload<Writer>::template PartialField<T>();

        // compile-time binding
        try
        {
            exception = false;
            Deserialize<Reader, Type, Type>(payload);
        }
        catch(bond::CoreException&)
        {
            exception = true;
        }
        catch(bond::StreamException&)
        {
            UT_AssertIsTrue(bond::uses_static_parser<Reader>::value);
            exception = true;
        }

        UT_AssertIsTrue(exception);

        // runtime binding
        try
        {
            exception = false;
            Deserialize<Reader, Type, void>(payload);
        }
        catch(bond::CoreException&)
        {
            exception = true;
        }
        catch(bond::StreamException&)
        {
            UT_AssertIsTrue(bond::uses_static_parser<Reader>::value);
            exception = true;
        }

        UT_AssertIsTrue(exception);
    }
};


template <typename Reader, typename Writer>
TEST_CASE_BEGIN(MissingFieldException)
{
    boost::mpl::for_each<BasicTypes>(MissingFieldExceptionTest<Reader, Writer>());
    boost::mpl::for_each<SkipTypes<double>::type>(MissingFieldExceptionTest<Reader, Writer>());
}
TEST_CASE_END


template <typename Reader, typename Writer>
TEST_CASE_BEGIN(OomException)
{
    typedef BondStructOptional<std::vector<allocator_test::SimpleType, detail::TestAllocator<allocator_test::SimpleType> > > Type;

    bool        exception;
    bond::blob  payload;

    payload = InvalidPayload<Writer>::template OomField<std::vector<allocator_test::SimpleType> >();

    // compile-time binding
    try
    {
        exception = false;
        Deserialize<Reader, Type, Type>(payload);
    }
    catch(std::exception&)
    {
        exception = true;
    }

    UT_AssertIsTrue(exception);

    // runtime binding
    try
    {
        exception = false;
        Deserialize<Reader, Type, void>(payload);
    }
    catch(std::exception&)
    {
        exception = true;
    }

    UT_AssertIsTrue(exception);
}
TEST_CASE_END


struct transform_exception {};

class Exceptions
    : public bond::SerializingTransform
{
public:
    Exceptions(int when)
        : when(when)
    {}

    void Begin(const bond::Metadata&) const
    {
        MaybeThrow();
    }

    void End() const
    {
        MaybeThrow();
    }

    void UnknownEnd() const
    {
        MaybeThrow();
    }

    template <typename T>
    bool Base(const T& value) const
    {
        return Process(value);
    }

    template <typename T>
    bool Field(uint16_t, const bond::Metadata&, const T& value) const
    {
        return Process(value);
    }

    template <typename T>
    bool UnknownField(uint16_t, const T& value) const
    {
        return Process(value);
    }

    template <typename T>
    void Container(const T& element, uint32_t size) const
    {
        while (size--)
            Process(element);
    }

    template <typename Key, typename T>
    void Container(const Key& key, const T& value, uint32_t size) const
    {
        while (size--)
        {
            Process(key);
            Process(value);
        }
    }

private:
    template <typename Reader, typename T>
    typename boost::enable_if<bond::is_basic_type<T>, bool>::type
    Process(const bond::value<T, Reader>& value) const
    {
        MaybeThrow();
        value.Skip();
        return false;
    }

    template <typename Reader, typename T>
    typename boost::disable_if<bond::is_basic_type<T>, bool>::type
    Process(const bond::value<T, Reader>& value) const
    {
        MaybeThrow();
        Apply(*this, value);
        return false;
    }

    template <typename Reader, typename T>
    bool Process(const bond::bonded<T, Reader>& value) const
    {
        MaybeThrow();
        Apply(*this, value);
        return false;
    }

    void MaybeThrow() const
    {
        if (!when--)
            throw transform_exception();
    }

    mutable int when;
};


template <typename Reader, typename Writer, typename T>
TEST_CASE_BEGIN(TransformException)
{
    typename Writer::Buffer buffer(4096);

    Factory<Writer>::Call(buffer, bond::v1, boost::bind(
        bond::Serialize<bond::BuiltInProtocols, T, Writer>, InitRandom<T>(2, 2), boost::placeholders::_1));

    {
        for (int i = 0;; ++i)
        {
            try
            {
                Reader reader(buffer.GetBuffer());
                UT_AssertIsTrue(i < 5000);
                Exceptions exceptions(i);
                Apply(exceptions, bond::bonded<T, Reader&>(reader));
                UT_AssertIsTrue(i > 0);
                break;
            }
            catch(const transform_exception&)
            {
            }
        }

        for (int i = 0;; ++i)
        {
            try
            {
                Reader reader(buffer.GetBuffer());
                UT_AssertIsTrue(i < 5000);
                Exceptions exceptions(i);
                Apply(exceptions, bond::bonded<void, Reader&>(reader, bond::GetRuntimeSchema<T>()));
                UT_AssertIsTrue(i > 0);
                break;
            }
            catch(const transform_exception&)
            {
            }
        }
    }
}
TEST_CASE_END

template <uint16_t N, typename Reader, typename Writer>
void ExceptionTests(const char* name)
{
    UnitTestSuite suite(name);

    AddTestCase<TEST_ID(N),
        OomException, Reader, Writer>(suite, "Out of memory");

    AddTestCase<TEST_ID(N),
        MissingFieldException, Reader, Writer>(suite, "Eof after missing field");

    AddTestCase<TEST_ID(N),
        TransformException, Reader, Writer, NestedListsStruct>(suite, "Exceptions in transform");
}

template <typename Reader, typename DeserializedType>
void ValidateReaderPayloadException(const bond::blob& payload)
{
    bond::InputBuffer buffer { payload };
    Reader reader(buffer);
    try
    {
        DeserializedType obj;
        Deserialize(reader, obj);
        UT_AssertIsTrue(false);
    }
    catch (bond::Exception&) {}
}

template <typename Reader, typename DeserializedType, uint16_t Protocol>
void ValidateReaderPayloadException(const bond::blob& payload)
{
    bond::InputBuffer buffer { payload };
    Reader reader(buffer, Protocol);
    try
    {
        DeserializedType obj;
        Deserialize(reader, obj);
        UT_AssertIsTrue(false);
    }
    catch (bond::Exception&){}
}

TEST_CASE_BEGIN(ExceptionTests_CompactBinary_LargeContainerInvalidTypes_TestCase)
{
    const bond::blob payloads[] =
    {
        BlobFromStringLiteral("\x8b\x1e\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_LIST<0x1e>
        BlobFromStringLiteral("\x8b\x8d\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_LIST<0x8d>
        BlobFromStringLiteral("\x8c\x1e\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_SET<0x1e>
        BlobFromStringLiteral("\x8c\x8d\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_SET<0x8d>
        BlobFromStringLiteral("\x8d\x1e\x07\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<0x1e, BT_FLOAT>
        BlobFromStringLiteral("\x8d\x8d\x07\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<0x8d, BT_FLOAT>
        BlobFromStringLiteral("\x8d\x07\x1e\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<BT_FLOAT, 0x1e>
        BlobFromStringLiteral("\x8d\x07\x8d\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<BT_FLOAT, 0x8d>
    };

    for (const bond::blob& payload : payloads)
    {
        ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedStruct, 1>(payload);
        ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedStruct, 2>(payload);
    }
}
TEST_CASE_END

TEST_CASE_BEGIN(ExceptionTests_FastBinary_LargeContainerInvalidTypes_TestCase)
{
    const bond::blob payloads[] =
    {
        BlobFromStringLiteral("\x0b\x04\x00\x1e\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_LIST<0x1e>
        BlobFromStringLiteral("\x0b\x04\x00\x8d\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_LIST<0x8d>
        BlobFromStringLiteral("\x0c\x04\x00\x1e\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_SET<0x1e>
        BlobFromStringLiteral("\x0c\x04\x00\x8d\xff\xff\xff\xff\x07\xff\xff\xff\xff"),     // BT_SET<0x8d>
        BlobFromStringLiteral("\x0d\x04\x00\x1e\x07\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<0x1e, BT_FLOAT>
        BlobFromStringLiteral("\x0d\x04\x00\x8d\x07\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<0x8d, BT_FLOAT>
        BlobFromStringLiteral("\x0d\x04\x00\x07\x1e\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<BT_FLOAT, 0x1e>
        BlobFromStringLiteral("\x0d\x04\x00\x07\x8d\xff\xff\xff\xff\x07\xff\xff\xff\xff"), // BT_MAP<BT_FLOAT, 0x8d>
    };

    for (const bond::blob& payload : payloads)
    {
        ValidateReaderPayloadException<bond::FastBinaryReader<bond::InputBuffer>, NestedStruct>(payload);
    }
}
TEST_CASE_END

TEST_CASE_BEGIN(ExceptionTests_SimpleBinary_RecursionViaNesting)
{
    // Manually-constructed payload with a nesting depth of 200
    const size_t Depth = 200;
    std::vector<uint8_t> payloadBuffer;
    payloadBuffer.reserve(Depth * 8);
    for (int i = 0; i < Depth; i++)
    {
        payloadBuffer.push_back(0xe8);
        payloadBuffer.push_back(0x03);
        payloadBuffer.push_back(0x00);
        payloadBuffer.push_back(0x00);
        payloadBuffer.push_back(0x01);
        payloadBuffer.push_back(0x00);
        payloadBuffer.push_back(0x00);
        payloadBuffer.push_back(0x00);
    }

    bond::blob payload(payloadBuffer.data(), static_cast<uint32_t>(payloadBuffer.size()));
    ValidateReaderPayloadException<bond::SimpleBinaryReader<bond::InputBuffer>, StructWithRecursiveReference>(payload);
}
TEST_CASE_END

TEST_CASE_BEGIN(ExceptionTests_CompactBinary_RecursionViaSkip)
{
    // Depth 130, BT_STRUCT, Protocol v1
    ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedWithBase1, 1>(
        BlobFromStringLiteral("\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\xCA\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"));

    // Depth 130, BT_LIST, Protocol v1
    ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedWithBase1, 1>(
        BlobFromStringLiteral("\xCB\x32\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x64\x00"));

    // Depth 130, BT_LIST, Protocol v2
    ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedWithBase1, 2>(
        BlobFromStringLiteral("\x86\x01\xCB\x32\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x4B\x64\x00"));

    // Depth 130, BT_SET, Protocol v1
    ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedWithBase1, 1>(
        BlobFromStringLiteral("\xCC\x32\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x64\x00"));

    // Depth 130, BT_SET, Protocol v2
    ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedWithBase1, 2>(
        BlobFromStringLiteral("\x86\x01\xCC\x32\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x4C\x64\x00"));

    // Depth 130, BT_MAP, Protocol v1
    ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedWithBase1, 1>(
        BlobFromStringLiteral("\xCD\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x32\x64\x00"));

    // Depth 130, BT_MAP, Protocol v2
    ValidateReaderPayloadException<bond::CompactBinaryReader<bond::InputBuffer>, NestedWithBase1, 2>(
        BlobFromStringLiteral("\x8D\x04\xCD\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x32\x64\x00"));
}
TEST_CASE_END

TEST_CASE_BEGIN(ExceptionTests_FastBinary_RecursionViaSkip)
{
    // Depth 130, BT_STRUCT, Protocol v1
    ValidateReaderPayloadException<bond::FastBinaryReader<bond::InputBuffer>, NestedWithBase1>(
        BlobFromStringLiteral("\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x0A\x32\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"));

    // Depth 130, BT_LIST, Protocol v1
    ValidateReaderPayloadException<bond::FastBinaryReader<bond::InputBuffer>, NestedWithBase1>(
        BlobFromStringLiteral("\x0B\x32\x00\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x0B\x01\x64\x00"));

    // Depth 130, BT_SET, Protocol v1
    ValidateReaderPayloadException<bond::FastBinaryReader<bond::InputBuffer>, NestedWithBase1>(
        BlobFromStringLiteral("\x0C\x32\x00\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x0C\x01\x64\x00"));

    // Depth 130, BT_MAP, Protocol v1
    ValidateReaderPayloadException<bond::FastBinaryReader<bond::InputBuffer>, NestedWithBase1>(
        BlobFromStringLiteral("\x0D\x32\x00\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x0E\x0D\x01\x32\x32\x64\x00"));
}
TEST_CASE_END

TEST_CASE_BEGIN(ExceptionTests_SetDeserializeMaxDepth_ImpactsValidWorkloads)
{
    NestedStruct3 from;
    InitRandom(from);

    // Default max depth
    {
        bond::OutputBuffer output;
        bond::CompactBinaryWriter<bond::OutputBuffer> writer(output);
        Serialize(from, writer);

        bond::CompactBinaryReader<bond::InputBuffer> reader(output.GetBuffer());
        NestedStruct3 to;
        Deserialize(reader, to);
        UT_Equal(from, to);
    }

    // Lower the depth to the point where a valid workload fails
    {
        bond::OutputBuffer output;
        bond::CompactBinaryWriter<bond::OutputBuffer> writer(output);
        Serialize(from, writer);

        bool exception = false;
        try
        {
            bond::SetDeserializeMaxDepth(1);

            bond::CompactBinaryReader<bond::InputBuffer> reader(output.GetBuffer());
            NestedStruct3 to;
            Deserialize(reader, to);
        }
        catch (bond::CoreException&)
        {
            exception = true;
        }
        UT_AssertIsTrue(exception);
    }

    // Put the depth back and validate the workload works again
    {
        bond::OutputBuffer output;
        bond::CompactBinaryWriter<bond::OutputBuffer> writer(output);
        Serialize(from, writer);

        bond::SetDeserializeMaxDepth(64);

        bond::CompactBinaryReader<bond::InputBuffer> reader(output.GetBuffer());
        NestedStruct3 to;
        Deserialize(reader, to);
        UT_Equal(from, to);
    }
}
TEST_CASE_END

template <uint16_t N>
void ExceptionTests_SimpleBinary_BadPayload(const char* name)
{
    UnitTestSuite suite(name);

    AddTestCase<TEST_ID(N),
        ExceptionTests_SimpleBinary_RecursionViaNesting>(suite, "Recursion via nesting");
}

template <uint16_t N>
void ExceptionTests_CompactBinary_BadPayload(const char* name)
{
    UnitTestSuite suite(name);

    AddTestCase<TEST_ID(N),
        ExceptionTests_CompactBinary_LargeContainerInvalidTypes_TestCase>(suite, "Large container of invalid types");

    AddTestCase<TEST_ID(N),
        ExceptionTests_CompactBinary_RecursionViaSkip>(suite, "Recursion via Skip");
}

template <uint16_t N>
void ExceptionTests_FastBinary_BadPayload(const char* name)
{
    UnitTestSuite suite(name);

    AddTestCase<TEST_ID(N),
        ExceptionTests_FastBinary_LargeContainerInvalidTypes_TestCase>(suite, "Large container of invalid types");

    AddTestCase<TEST_ID(N),
        ExceptionTests_FastBinary_RecursionViaSkip>(suite, "Recursion via Skip");
}

template <uint16_t N>
void ExceptionTests_SetDeserializeMaxDepth(const char* name)
{
    UnitTestSuite suite(name);

    AddTestCase<TEST_ID(N),
        ExceptionTests_SetDeserializeMaxDepth_ImpactsValidWorkloads>(suite, "Test that SetDeserializeMaxDepth has an effect");
}

void ExceptionTest::Initialize()
{
    TEST_SIMPLE_PROTOCOL(
        ExceptionTests<
            0x1501,
            bond::SimpleBinaryReader<bond::InputBuffer>,
            bond::SimpleBinaryWriter<bond::OutputBuffer> >("Exception tests for SimpleBinary");
    );

    TEST_COMPACT_BINARY_PROTOCOL(
        ExceptionTests<
            0x1502,
            bond::CompactBinaryReader<bond::InputBuffer>,
            bond::CompactBinaryWriter<bond::OutputBuffer> >("Exception tests for CompactBinary");
    );

    TEST_FAST_BINARY_PROTOCOL(
        ExceptionTests<
            0x1503,
            bond::FastBinaryReader<bond::InputBuffer>,
            bond::FastBinaryWriter<bond::OutputBuffer> >("Exception tests for FastBinary");
    );

    TEST_SIMPLE_PROTOCOL(
        ExceptionTests_SimpleBinary_BadPayload<
        0x1504 >("Exception tests with bad payloads specific to SimpleBinary");
    );

    TEST_COMPACT_BINARY_PROTOCOL(
        ExceptionTests_CompactBinary_BadPayload<
        0x1505 >("Exception tests with bad payloads specific to CompactBinary");
    );

    TEST_FAST_BINARY_PROTOCOL(
        ExceptionTests_FastBinary_BadPayload<
        0x1506 >("Exception tests with bad payloads specific to FastBinary");
    );

    TEST_COMPACT_BINARY_PROTOCOL(
        ExceptionTests_SetDeserializeMaxDepth<
        0x1507 >("Exception tests validating the behavior of SetDeserializeMaxDepth");
    );
}


bool init_unit_test()
{
    ExceptionTest::Initialize();
    return true;
}
